// -*- mode:c++ -*-

// Copyright (c) 2015 RISC-V Foundation
// Copyright (c) 2016 The University of Virginia
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Alec Roelke

////////////////////////////////////////////////////////////////////
//
// The RISC-V ISA decoder
//

decode OPCODE default Unknown::unknown() {
    0x03: decode FUNCT3 {
        format Load {
            0x0: lb({{
                Rd_sd = Mem_sb;
            }});
            0x1: lh({{
                Rd_sd = Mem_sh;
            }});
            0x2: lw({{
                Rd_sd = Mem_sw;
            }});
            0x3: ld({{
                Rd_sd = Mem_sd;
            }});
            0x4: lbu({{
                Rd = Mem_ub;
            }});
            0x5: lhu({{
                Rd = Mem_uh;
            }});
            0x6: lwu({{
                Rd = Mem_uw;
            }});
        }
    }

    0x07: decode FUNCT3 {
        format Load {
            0x2: flw({{
                Fd_bits = (uint64_t)Mem_uw;
            }});
            0x3: fld({{
                Fd_bits = Mem;
            }});
        }
    }

    0x0f: decode FUNCT3 {
        format IOp {
            0x0: fence({{
            }}, IsNonSpeculative, IsMemBarrier, No_OpClass);
            0x1: fence_i({{
            }}, IsNonSpeculative, IsSerializeAfter, No_OpClass);
        }
    }

    0x13: decode FUNCT3 {
        format IOp {
            0x0: addi({{
                Rd_sd = Rs1_sd + imm;
            }});
            0x1: slli({{
                Rd = Rs1 << SHAMT6;
            }});
            0x2: slti({{
                Rd = (Rs1_sd < imm) ? 1 : 0;
            }});
            0x3: sltiu({{
                Rd = (Rs1 < (uint64_t)imm) ? 1 : 0;
            }});
            0x4: xori({{
                Rd = Rs1 ^ (uint64_t)imm;
            }});
            0x5: decode SRTYPE {
                0x0: srli({{
                    Rd = Rs1 >> SHAMT6;
                }});
                0x1: srai({{
                    Rd_sd = Rs1_sd >> SHAMT6;
                }});
            }
            0x6: ori({{
                Rd = Rs1 | (uint64_t)imm;
            }});
            0x7: andi({{
                Rd = Rs1 & (uint64_t)imm;
            }});
        }
    }

    0x17: UOp::auipc({{
        Rd = PC + imm;
    }});

    0x1b: decode FUNCT3 {
        format IOp {
            0x0: addiw({{
                Rd_sd = (int32_t)Rs1 + (int32_t)imm;
            }});
            0x1: slliw({{
                Rd_sd = Rs1_sw << SHAMT5;
            }});
            0x5: decode SRTYPE {
                0x0: srliw({{
                    Rd = Rs1_uw >> SHAMT5;
                }});
                0x1: sraiw({{
                    Rd_sd = Rs1_sw >> SHAMT5;
                }});
            }
        }
    }

    0x23: decode FUNCT3 {
        format Store {
            0x0: sb({{
                Mem_ub = Rs2_ub;
            }});
            0x1: sh({{
                Mem_uh = Rs2_uh;
            }});
            0x2: sw({{
                Mem_uw = Rs2_uw;
            }});
            0x3: sd({{
                Mem_ud = Rs2_ud;
            }});
        }
    }

    0x27: decode FUNCT3 {
        format Store {
            0x2: fsw({{
                Mem_uw = (uint32_t)Fs2_bits;
            }});
            0x3: fsd({{
                Mem_ud = Fs2_bits;
            }});
        }
    }

    0x2f: decode FUNCT3 {
        0x2: decode AMOFUNCT {
            0x2: LoadReserved::lr_w({{
                Rd_sd = Mem_sw;
            }}, mem_flags=LLSC);
            0x3: StoreCond::sc_w({{
                Mem_uw = Rs2_uw;
            }}, {{
                Rd = result;
            }}, inst_flags=IsStoreConditional, mem_flags=LLSC);
            format AtomicMemOp {
                0x0: amoadd_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = Rs2_sw + Rt_sd;
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x1: amoswap_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = Rs2_uw;
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x4: amoxor_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = Rs2_uw^Rt_sd;
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x8: amoor_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = Rs2_uw | Rt_sd;
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0xc: amoand_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = Rs2_uw&Rt_sd;
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x10: amomin_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = std::min<int32_t>(Rs2_sw, Rt_sd);
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x14: amomax_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = std::max<int32_t>(Rs2_sw, Rt_sd);
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x18: amominu_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = std::min<uint32_t>(Rs2_uw, Rt_sd);
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x1c: amomaxu_w({{Rt_sd = Mem_sw;}}, {{
                    Mem_sw = std::max<uint32_t>(Rs2_uw, Rt_sd);
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
            }
        }
        0x3: decode AMOFUNCT {
            0x2: LoadReserved::lr_d({{
                Rd_sd = Mem_sd;
            }}, mem_flags=LLSC);
            0x3: StoreCond::sc_d({{
                Mem = Rs2;
            }}, {{
                Rd = result;
            }}, mem_flags=LLSC, inst_flags=IsStoreConditional);
            format AtomicMemOp {
                0x0: amoadd_d({{Rt_sd = Mem_sd;}}, {{
                    Mem_sd = Rs2_sd + Rt_sd;
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x1: amoswap_d({{Rt = Mem;}}, {{
                    Mem = Rs2;
                    Rd = Rt;
                }}, {{EA = Rs1;}});
                0x4: amoxor_d({{Rt = Mem;}}, {{
                    Mem = Rs2^Rt;
                    Rd = Rt;
                }}, {{EA = Rs1;}});
                0x8: amoor_d({{Rt = Mem;}}, {{
                    Mem = Rs2 | Rt;
                    Rd = Rt;
                }}, {{EA = Rs1;}});
                0xc: amoand_d({{Rt = Mem;}}, {{
                    Mem = Rs2&Rt;
                    Rd = Rt;
                }}, {{EA = Rs1;}});
                0x10: amomin_d({{Rt_sd = Mem_sd;}}, {{
                    Mem_sd = std::min(Rs2_sd, Rt_sd);
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x14: amomax_d({{Rt_sd = Mem_sd;}}, {{
                    Mem_sd = std::max(Rs2_sd, Rt_sd);
                    Rd_sd = Rt_sd;
                }}, {{EA = Rs1;}});
                0x18: amominu_d({{Rt = Mem;}}, {{
                    Mem = std::min(Rs2, Rt);
                    Rd = Rt;
                }}, {{EA = Rs1;}});
                0x1c: amomaxu_d({{Rt = Mem;}}, {{
                    Mem = std::max(Rs2, Rt);
                    Rd = Rt;
                }}, {{EA = Rs1;}});
            }
        }
    }
    0x33: decode FUNCT3 {
        format ROp {
            0x0: decode FUNCT7 {
                0x0: add({{
                    Rd = Rs1_sd + Rs2_sd;
                }});
                0x1: mul({{
                    Rd = Rs1_sd*Rs2_sd;
                }}, IntMultOp);
                0x20: sub({{
                    Rd = Rs1_sd - Rs2_sd;
                }});
            }
            0x1: decode FUNCT7 {
                0x0: sll({{
                    Rd = Rs1 << Rs2<5:0>;
                }});
                0x1: mulh({{
                    bool negate = (Rs1_sd < 0) != (Rs2_sd < 0);

                    uint64_t Rs1_lo = (uint32_t)std::abs(Rs1_sd);
                    uint64_t Rs1_hi = (uint64_t)std::abs(Rs1_sd) >> 32;
                    uint64_t Rs2_lo = (uint32_t)std::abs(Rs2_sd);
                    uint64_t Rs2_hi = (uint64_t)std::abs(Rs2_sd) >> 32;

                    uint64_t hi = Rs1_hi*Rs2_hi;
                    uint64_t mid1 = Rs1_hi*Rs2_lo;
                    uint64_t mid2 = Rs1_lo*Rs2_hi;
                    uint64_t lo = Rs2_lo*Rs1_lo;
                    uint64_t carry = ((uint64_t)(uint32_t)mid1
                            + (uint64_t)(uint32_t)mid2 + (lo >> 32)) >> 32;

                    uint64_t res = hi + (mid1 >> 32) + (mid2 >> 32) + carry;
                    Rd = negate ? ~res + (Rs1_sd*Rs2_sd == 0 ? 1 : 0) : res;
                }}, IntMultOp);
            }
            0x2: decode FUNCT7 {
                0x0: slt({{
                    Rd = (Rs1_sd < Rs2_sd) ? 1 : 0;
                }});
                0x1: mulhsu({{
                    bool negate = Rs1_sd < 0;
                    uint64_t Rs1_lo = (uint32_t)std::abs(Rs1_sd);
                    uint64_t Rs1_hi = (uint64_t)std::abs(Rs1_sd) >> 32;
                    uint64_t Rs2_lo = (uint32_t)Rs2;
                    uint64_t Rs2_hi = Rs2 >> 32;

                    uint64_t hi = Rs1_hi*Rs2_hi;
                    uint64_t mid1 = Rs1_hi*Rs2_lo;
                    uint64_t mid2 = Rs1_lo*Rs2_hi;
                    uint64_t lo = Rs1_lo*Rs2_lo;
                    uint64_t carry = ((uint64_t)(uint32_t)mid1
                            + (uint64_t)(uint32_t)mid2 + (lo >> 32)) >> 32;

                    uint64_t res = hi + (mid1 >> 32) + (mid2 >> 32) + carry;
                    Rd = negate ? ~res + (Rs1_sd*Rs2 == 0 ? 1 : 0) : res;
                }}, IntMultOp);
            }
            0x3: decode FUNCT7 {
                0x0: sltu({{
                    Rd = (Rs1 < Rs2) ? 1 : 0;
                }});
                0x1: mulhu({{
                    uint64_t Rs1_lo = (uint32_t)Rs1;
                    uint64_t Rs1_hi = Rs1 >> 32;
                    uint64_t Rs2_lo = (uint32_t)Rs2;
                    uint64_t Rs2_hi = Rs2 >> 32;

                    uint64_t hi = Rs1_hi*Rs2_hi;
                    uint64_t mid1 = Rs1_hi*Rs2_lo;
                    uint64_t mid2 = Rs1_lo*Rs2_hi;
                    uint64_t lo = Rs1_lo*Rs2_lo;
                    uint64_t carry = ((uint64_t)(uint32_t)mid1
                            + (uint64_t)(uint32_t)mid2 + (lo >> 32)) >> 32;

                    Rd = hi + (mid1 >> 32) + (mid2 >> 32) + carry;
                }}, IntMultOp);
            }
            0x4: decode FUNCT7 {
                0x0: xor({{
                    Rd = Rs1 ^ Rs2;
                }});
                0x1: div({{
                    if (Rs2_sd == 0) {
                        Rd_sd = -1;
                    } else if (Rs1_sd == std::numeric_limits<int64_t>::min()
                            && Rs2_sd == -1) {
                        Rd_sd = std::numeric_limits<int64_t>::min();
                    } else {
                        Rd_sd = Rs1_sd/Rs2_sd;
                    }
                }}, IntDivOp);
            }
            0x5: decode FUNCT7 {
                0x0: srl({{
                    Rd = Rs1 >> Rs2<5:0>;
                }});
                0x1: divu({{
                    if (Rs2 == 0) {
                        Rd = std::numeric_limits<uint64_t>::max();
                    } else {
                        Rd = Rs1/Rs2;
                    }
                }}, IntDivOp);
                0x20: sra({{
                    Rd_sd = Rs1_sd >> Rs2<5:0>;
                }});
            }
            0x6: decode FUNCT7 {
                0x0: or({{
                    Rd = Rs1 | Rs2;
                }});
                0x1: rem({{
                    if (Rs2_sd == 0) {
                        Rd = Rs1_sd;
                    } else if (Rs1_sd == std::numeric_limits<int64_t>::min()
                            && Rs2_sd == -1) {
                        Rd = 0;
                    } else {
                        Rd = Rs1_sd%Rs2_sd;
                    }
                }}, IntDivOp);
            }
            0x7: decode FUNCT7 {
                0x0: and({{
                    Rd = Rs1 & Rs2;
                }});
                0x1: remu({{
                    if (Rs2 == 0) {
                        Rd = Rs1;
                    } else {
                        Rd = Rs1%Rs2;
                    }
                }}, IntDivOp);
            }
        }
    }

    0x37: UOp::lui({{
        Rd = (uint64_t)imm;
    }});

    0x3b: decode FUNCT3 {
        format ROp {
            0x0: decode FUNCT7 {
                0x0: addw({{
                    Rd_sd = Rs1_sw + Rs2_sw;
                }});
                0x1: mulw({{
                    Rd_sd = (int32_t)(Rs1_sw*Rs2_sw);
                }}, IntMultOp);
                0x20: subw({{
                    Rd_sd = Rs1_sw - Rs2_sw;
                }});
            }
            0x1: sllw({{
                Rd_sd = Rs1_sw << Rs2<4:0>;
            }});
            0x4: divw({{
                if (Rs2_sw == 0) {
                    Rd_sd = -1;
                } else if (Rs1_sw == std::numeric_limits<int32_t>::min()
                        && Rs2_sw == -1) {
                    Rd_sd = std::numeric_limits<int32_t>::min();
                } else {
                    Rd_sd = Rs1_sw/Rs2_sw;
                }
            }}, IntDivOp);
            0x5: decode FUNCT7 {
                0x0: srlw({{
                    Rd_uw = Rs1_uw >> Rs2<4:0>;
                }});
                0x1: divuw({{
                    if (Rs2_uw == 0) {
                        Rd_sd = std::numeric_limits<IntReg>::max();
                    } else {
                        Rd_sd = (int32_t)(Rs1_uw/Rs2_uw);
                    }
                }}, IntDivOp);
                0x20: sraw({{
                    Rd_sd = Rs1_sw >> Rs2<4:0>;
                }});
            }
            0x6: remw({{
                if (Rs2_sw == 0) {
                    Rd_sd = Rs1_sw;
                } else if (Rs1_sw == std::numeric_limits<int32_t>::min()
                        && Rs2_sw == -1) {
                    Rd_sd = 0;
                } else {
                    Rd_sd = Rs1_sw%Rs2_sw;
                }
            }}, IntDivOp);
            0x7: remuw({{
                if (Rs2_uw == 0) {
                    Rd_sd = (int32_t)Rs1_uw;
                } else {
                    Rd_sd = (int32_t)(Rs1_uw%Rs2_uw);
                }
            }}, IntDivOp);
        }
    }

    format FPR4Op {
        0x43: decode FUNCT2 {
            0x0: fmadd_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fs3 = reinterpret_cast<float&>(temp = Fs3_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2) || std::isnan(fs3)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)
                            || issignalingnan(fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else if (std::isinf(fs1) || std::isinf(fs2) ||
                        std::isinf(fs3)) {
                    if (std::signbit(fs1) == std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = std::numeric_limits<float>::infinity();
                    } else if (std::signbit(fs1) != std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = -std::numeric_limits<float>::infinity();
                    } else { // Fs3_sf is infinity
                        fd = fs3;
                    }
                } else {
                    fd = fs1*fs2 + fs3;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatMultOp);
            0x1: fmadd_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2) || std::isnan(Fs3)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)
                            || issignalingnan(Fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else if (std::isinf(Fs1) || std::isinf(Fs2) ||
                        std::isinf(Fs3)) {
                    if (std::signbit(Fs1) == std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = std::numeric_limits<double>::infinity();
                    } else if (std::signbit(Fs1) != std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = -std::numeric_limits<double>::infinity();
                    } else {
                        Fd = Fs3;
                    }
                } else {
                    Fd = Fs1*Fs2 + Fs3;
                }
            }}, FloatMultOp);
        }
        0x47: decode FUNCT2 {
            0x0: fmsub_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fs3 = reinterpret_cast<float&>(temp = Fs3_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2) || std::isnan(fs3)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)
                            || issignalingnan(fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else if (std::isinf(fs1) || std::isinf(fs2) ||
                        std::isinf(fs3)) {
                    if (std::signbit(fs1) == std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = std::numeric_limits<float>::infinity();
                    } else if (std::signbit(fs1) != std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = -std::numeric_limits<float>::infinity();
                    } else { // Fs3_sf is infinity
                        fd = -fs3;
                    }
                } else {
                    fd = fs1*fs2 - fs3;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatMultOp);
            0x1: fmsub_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2) || std::isnan(Fs3)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)
                            || issignalingnan(Fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else if (std::isinf(Fs1) || std::isinf(Fs2) ||
                        std::isinf(Fs3)) {
                    if (std::signbit(Fs1) == std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = std::numeric_limits<double>::infinity();
                    } else if (std::signbit(Fs1) != std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = -std::numeric_limits<double>::infinity();
                    } else {
                        Fd = -Fs3;
                    }
                } else {
                    Fd = Fs1*Fs2 - Fs3;
                }
            }}, FloatMultOp);
        }
        0x4b: decode FUNCT2 {
            0x0: fnmsub_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fs3 = reinterpret_cast<float&>(temp = Fs3_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2) || std::isnan(fs3)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)
                            || issignalingnan(fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else if (std::isinf(fs1) || std::isinf(fs2) ||
                        std::isinf(fs3)) {
                    if (std::signbit(fs1) == std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = -std::numeric_limits<float>::infinity();
                    } else if (std::signbit(fs1) != std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = std::numeric_limits<float>::infinity();
                    } else { // Fs3_sf is infinity
                        fd = fs3;
                    }
                } else {
                    fd = -(fs1*fs2 - fs3);
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatMultOp);
            0x1: fnmsub_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2) || std::isnan(Fs3)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)
                            || issignalingnan(Fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else if (std::isinf(Fs1) || std::isinf(Fs2)
                        || std::isinf(Fs3)) {
                    if (std::signbit(Fs1) == std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = -std::numeric_limits<double>::infinity();
                    } else if (std::signbit(Fs1) != std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = std::numeric_limits<double>::infinity();
                    } else {
                        Fd = Fs3;
                    }
                } else {
                    Fd = -(Fs1*Fs2 - Fs3);
                }
            }}, FloatMultOp);
        }
        0x4f: decode FUNCT2 {
            0x0: fnmadd_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fs3 = reinterpret_cast<float&>(temp = Fs3_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2) || std::isnan(fs3)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)
                            || issignalingnan(fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else if (std::isinf(fs1) || std::isinf(fs2) ||
                        std::isinf(fs3)) {
                    if (std::signbit(fs1) == std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = -std::numeric_limits<float>::infinity();
                    } else if (std::signbit(fs1) != std::signbit(fs2)
                            && !std::isinf(fs3)) {
                        fd = std::numeric_limits<float>::infinity();
                    } else { // Fs3_sf is infinity
                        fd = -fs3;
                    }
                } else {
                    fd = -(fs1*fs2 + fs3);
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatMultOp);
            0x1: fnmadd_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2) || std::isnan(Fs3)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)
                            || issignalingnan(Fs3)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else if (std::isinf(Fs1) || std::isinf(Fs2) ||
                        std::isinf(Fs3)) {
                    if (std::signbit(Fs1) == std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = -std::numeric_limits<double>::infinity();
                    } else if (std::signbit(Fs1) != std::signbit(Fs2)
                            && !std::isinf(Fs3)) {
                        Fd = std::numeric_limits<double>::infinity();
                    } else {
                        Fd = -Fs3;
                    }
                } else {
                    Fd = -(Fs1*Fs2 + Fs3);
                }
            }}, FloatMultOp);
        }
    }

    0x53: decode FUNCT7 {
        format FPROp {
            0x0: fadd_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else {
                    fd = fs1 + fs2;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatAddOp);
            0x1: fadd_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else {
                    Fd = Fs1 + Fs2;
                }
            }}, FloatAddOp);
            0x4: fsub_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else {
                    fd = fs1 - fs2;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatAddOp);
            0x5: fsub_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else {
                    Fd = Fs1 - Fs2;
                }
            }}, FloatAddOp);
            0x8: fmul_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else {
                    fd = fs1*fs2;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatMultOp);
            0x9: fmul_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else {
                    Fd = Fs1*Fs2;
                }
            }}, FloatMultOp);
            0xc: fdiv_s({{
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                float fd;

                if (std::isnan(fs1) || std::isnan(fs2)) {
                    if (issignalingnan(fs1) || issignalingnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    fd = std::numeric_limits<float>::quiet_NaN();
                } else {
                    fd = fs1/fs2;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatDivOp);
            0xd: fdiv_d({{
                if (std::isnan(Fs1) || std::isnan(Fs2)) {
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Fd = std::numeric_limits<double>::quiet_NaN();
                } else {
                    Fd = Fs1/Fs2;
                }
            }}, FloatDivOp);
            0x10: decode ROUND_MODE {
                0x0: fsgnj_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                    float fd;

                    if (issignalingnan(fs1)) {
                        fd = std::numeric_limits<float>::signaling_NaN();
                        std::feclearexcept(FE_INVALID);
                    } else {
                        fd = std::copysign(fs1, fs2);
                    }
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
                }});
                0x1: fsgnjn_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                    float fd;

                    if (issignalingnan(fs1)) {
                        fd = std::numeric_limits<float>::signaling_NaN();
                        std::feclearexcept(FE_INVALID);
                    } else {
                        fd = std::copysign(fs1, -fs2);
                    }
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
                }});
                0x2: fsgnjx_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                    float fd;

                    if (issignalingnan(fs1)) {
                        fd = std::numeric_limits<float>::signaling_NaN();
                        std::feclearexcept(FE_INVALID);
                    } else {
                        fd = fs1*(std::signbit(fs2) ? -1.0 : 1.0);
                    }
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
                }});
            }
            0x11: decode ROUND_MODE {
                0x0: fsgnj_d({{
                    if (issignalingnan(Fs1)) {
                        Fd = std::numeric_limits<double>::signaling_NaN();
                        std::feclearexcept(FE_INVALID);
                    } else {
                        Fd = std::copysign(Fs1, Fs2);
                    }
                }});
                0x1: fsgnjn_d({{
                    if (issignalingnan(Fs1)) {
                        Fd = std::numeric_limits<double>::signaling_NaN();
                        std::feclearexcept(FE_INVALID);
                    } else {
                        Fd = std::copysign(Fs1, -Fs2);
                    }
                }});
                0x2: fsgnjx_d({{
                    if (issignalingnan(Fs1)) {
                        Fd = std::numeric_limits<double>::signaling_NaN();
                        std::feclearexcept(FE_INVALID);
                    } else {
                        Fd = Fs1*(std::signbit(Fs2) ? -1.0 : 1.0);
                    }
                }});
            }
            0x14: decode ROUND_MODE {
                0x0: fmin_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                    float fd;

                    if (issignalingnan(fs2)) {
                        fd = fs1;
                        FFLAGS |= FloatInvalid;
                    } else if (issignalingnan(fs1)) {
                        fd = fs2;
                        FFLAGS |= FloatInvalid;
                    } else {
                        fd = std::fmin(fs1, fs2);
                    }
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
                }}, FloatCmpOp);
                0x1: fmax_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);
                    float fd;

                    if (issignalingnan(fs2)) {
                        fd = fs1;
                        FFLAGS |= FloatInvalid;
                    } else if (issignalingnan(fs1)) {
                        fd = fs2;
                        FFLAGS |= FloatInvalid;
                    } else {
                        fd = std::fmax(fs1, fs2);
                    }
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
                }}, FloatCmpOp);
            }
            0x15: decode ROUND_MODE {
                0x0: fmin_d({{
                    if (issignalingnan(Fs2)) {
                        Fd = Fs1;
                        FFLAGS |= FloatInvalid;
                    } else if (issignalingnan(Fs1)) {
                        Fd = Fs2;
                        FFLAGS |= FloatInvalid;
                    } else {
                        Fd = std::fmin(Fs1, Fs2);
                    }
                }}, FloatCmpOp);
                0x1: fmax_d({{
                    if (issignalingnan(Fs2)) {
                        Fd = Fs1;
                        FFLAGS |= FloatInvalid;
                    } else if (issignalingnan(Fs1)) {
                        Fd = Fs2;
                        FFLAGS |= FloatInvalid;
                    } else {
                        Fd = std::fmax(Fs1, Fs2);
                    }
                }}, FloatCmpOp);
            }
            0x20: fcvt_s_d({{
                assert(CONV_SGN == 1);
                float fd;
                if (issignalingnan(Fs1)) {
                    fd = std::numeric_limits<float>::quiet_NaN();
                    FFLAGS |= FloatInvalid;
                } else {
                    fd = (float)Fs1;
                }
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatCvtOp);
            0x21: fcvt_d_s({{
                assert(CONV_SGN == 0);
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);

                if (issignalingnan(fs1)) {
                    Fd = std::numeric_limits<double>::quiet_NaN();
                    FFLAGS |= FloatInvalid;
                } else {
                    Fd = (double)fs1;
                }
            }}, FloatCvtOp);
            0x2c: fsqrt_s({{
                assert(RS2 == 0);
                uint32_t temp;
                float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                float fd;

                if (issignalingnan(Fs1_sf)) {
                    FFLAGS |= FloatInvalid;
                }
                fd = std::sqrt(fs1);
                Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(fd);
            }}, FloatSqrtOp);
            0x2d: fsqrt_d({{
                assert(RS2 == 0);
                Fd = std::sqrt(Fs1);
            }}, FloatSqrtOp);
            0x50: decode ROUND_MODE {
                0x0: fle_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);

                    if (std::isnan(fs1) || std::isnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                        Rd = 0;
                    } else {
                        Rd = fs1 <= fs2 ? 1 : 0;
                    }
                }}, FloatCmpOp);
                0x1: flt_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);

                    if (std::isnan(fs1) || std::isnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                        Rd = 0;
                    } else {
                        Rd = fs1 < fs2 ? 1 : 0;
                    }
                }}, FloatCmpOp);
                0x2: feq_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    float fs2 = reinterpret_cast<float&>(temp = Fs2_bits);

                    if (issignalingnan(fs1) || issignalingnan(fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Rd = fs1 == fs2 ? 1 : 0;
                }}, FloatCmpOp);
            }
            0x51: decode ROUND_MODE {
                0x0: fle_d({{
                    if (std::isnan(Fs1) || std::isnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                        Rd = 0;
                    } else {
                        Rd = Fs1 <= Fs2 ? 1 : 0;
                    }
                }}, FloatCmpOp);
                0x1: flt_d({{
                    if (std::isnan(Fs1) || std::isnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                        Rd = 0;
                    } else {
                        Rd = Fs1 < Fs2 ? 1 : 0;
                    }
                }}, FloatCmpOp);
                0x2: feq_d({{
                    if (issignalingnan(Fs1) || issignalingnan(Fs2)) {
                        FFLAGS |= FloatInvalid;
                    }
                    Rd = Fs1 == Fs2 ? 1 : 0;
                }}, FloatCmpOp);
            }
            0x60: decode CONV_SGN {
                0x0: fcvt_w_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);

                    if (std::isnan(fs1)) {
                        Rd_sd = std::numeric_limits<int32_t>::max();
                        FFLAGS |= FloatInvalid;
                    } else {
                        Rd_sd = (int32_t)fs1;
                        if (std::fetestexcept(FE_INVALID)) {
                            if (std::signbit(fs1)) {
                                Rd_sd = std::numeric_limits<int32_t>::min();
                            } else {
                                Rd_sd = std::numeric_limits<int32_t>::max();
                            }
                            std::feclearexcept(FE_INEXACT);
                        }
                    }
                }}, FloatCvtOp);
                0x1: fcvt_wu_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);

                    if (fs1 < 0.0) {
                        Rd = 0;
                        FFLAGS |= FloatInvalid;
                    } else {
                        Rd = (uint32_t)fs1;
                        if (std::fetestexcept(FE_INVALID)) {
                            Rd = std::numeric_limits<uint64_t>::max();
                            std::feclearexcept(FE_INEXACT);
                        }
                    }
                }}, FloatCvtOp);
                0x2: fcvt_l_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);

                    if (std::isnan(fs1)) {
                        Rd_sd = std::numeric_limits<int64_t>::max();
                        FFLAGS |= FloatInvalid;
                    } else {
                        Rd_sd = (int64_t)fs1;
                        if (std::fetestexcept(FE_INVALID)) {
                            if (std::signbit(fs1)) {
                                Rd_sd = std::numeric_limits<int64_t>::min();
                            } else {
                                Rd_sd = std::numeric_limits<int64_t>::max();
                            }
                            std::feclearexcept(FE_INEXACT);
                        }
                    }
                }}, FloatCvtOp);
                0x3: fcvt_lu_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);

                    if (fs1 < 0.0) {
                        Rd = 0;
                        FFLAGS |= FloatInvalid;
                    } else {
                        Rd = (uint64_t)fs1;
                        if (std::fetestexcept(FE_INVALID)) {
                            Rd = std::numeric_limits<uint64_t>::max();
                            std::feclearexcept(FE_INEXACT);
                        }
                    }
                }}, FloatCvtOp);
            }
            0x61: decode CONV_SGN {
                0x0: fcvt_w_d({{
                    Rd_sd = (int32_t)Fs1;
                    if (std::fetestexcept(FE_INVALID)) {
                        if (Fs1 < 0.0) {
                            Rd_sd = std::numeric_limits<int32_t>::min();
                        } else {
                            Rd_sd = std::numeric_limits<int32_t>::max();
                        }
                        std::feclearexcept(FE_INEXACT);
                    }
                }}, FloatCvtOp);
                0x1: fcvt_wu_d({{
                    if (Fs1 < 0.0) {
                        Rd = 0;
                        FFLAGS |= FloatInvalid;
                    } else {
                        Rd = (uint32_t)Fs1;
                        if (std::fetestexcept(FE_INVALID)) {
                            Rd = std::numeric_limits<uint64_t>::max();
                            std::feclearexcept(FE_INEXACT);
                        }
                    }
                }}, FloatCvtOp);
                0x2: fcvt_l_d({{
                    Rd_sd = Fs1;
                    if (std::fetestexcept(FE_INVALID)) {
                        if (Fs1 < 0.0) {
                            Rd_sd = std::numeric_limits<int64_t>::min();
                        } else {
                            Rd_sd = std::numeric_limits<int64_t>::max();
                        }
                        std::feclearexcept(FE_INEXACT);
                    }
                }}, FloatCvtOp);
                0x3: fcvt_lu_d({{
                    if (Fs1 < 0.0) {
                        Rd = 0;
                        FFLAGS |= FloatInvalid;
                    } else {
                        Rd = (uint64_t)Fs1;
                        if (std::fetestexcept(FE_INVALID)) {
                            Rd = std::numeric_limits<uint64_t>::max();
                            std::feclearexcept(FE_INEXACT);
                        }
                    }
                }}, FloatCvtOp);
            }
            0x68: decode CONV_SGN {
                0x0: fcvt_s_w({{
                    float temp = (float)Rs1_sw;
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(temp);
                }}, FloatCvtOp);
                0x1: fcvt_s_wu({{
                    float temp = (float)Rs1_uw;
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(temp);
                }}, FloatCvtOp);
                0x2: fcvt_s_l({{
                    float temp = (float)Rs1_sd;
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(temp);
                }}, FloatCvtOp);
                0x3: fcvt_s_lu({{
                    float temp = (float)Rs1;
                    Fd_bits = (uint64_t)reinterpret_cast<uint32_t&>(temp);
                }}, FloatCvtOp);
            }
            0x69: decode CONV_SGN {
                0x0: fcvt_d_w({{
                    Fd = (double)Rs1_sw;
                }}, FloatCvtOp);
                0x1: fcvt_d_wu({{
                    Fd = (double)Rs1_uw;
                }}, FloatCvtOp);
                0x2: fcvt_d_l({{
                    Fd = (double)Rs1_sd;
                }}, FloatCvtOp);
                0x3: fcvt_d_lu({{
                    Fd = (double)Rs1;
                }}, FloatCvtOp);
            }
            0x70: decode ROUND_MODE {
                0x0: fmv_x_s({{
                    Rd = (uint32_t)Fs1_bits;
                    if ((Rd&0x80000000) != 0) {
                        Rd |= (0xFFFFFFFFULL << 32);
                    }
                }}, FloatCvtOp);
                0x1: fclass_s({{
                    uint32_t temp;
                    float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
                    switch (std::fpclassify(fs1)) {
                    case FP_INFINITE:
                        if (std::signbit(fs1)) {
                            Rd = 1 << 0;
                        } else {
                            Rd = 1 << 7;
                        }
                        break;
                    case FP_NAN:
                        if (issignalingnan(fs1)) {
                            Rd = 1 << 8;
                        } else {
                            Rd = 1 << 9;
                        }
                        break;
                    case FP_ZERO:
                        if (std::signbit(fs1)) {
                            Rd = 1 << 3;
                        } else {
                            Rd = 1 << 4;
                        }
                        break;
                    case FP_SUBNORMAL:
                        if (std::signbit(fs1)) {
                            Rd = 1 << 2;
                        } else {
                            Rd = 1 << 5;
                        }
                        break;
                    case FP_NORMAL:
                        if (std::signbit(fs1)) {
                            Rd = 1 << 1;
                        } else {
                            Rd = 1 << 6;
                        }
                        break;
                    default:
                        panic("Unknown classification for operand.");
                        break;
                    }
                }});
            }
            0x71: decode ROUND_MODE {
                0x0: fmv_x_d({{
                    Rd = Fs1_bits;
                }}, FloatCvtOp);
                0x1: fclass_d({{
                    switch (std::fpclassify(Fs1)) {
                    case FP_INFINITE:
                        if (std::signbit(Fs1)) {
                            Rd = 1 << 0;
                        } else {
                            Rd = 1 << 7;
                        }
                        break;
                    case FP_NAN:
                        if (issignalingnan(Fs1)) {
                            Rd = 1 << 8;
                        } else {
                            Rd = 1 << 9;
                        }
                        break;
                    case FP_ZERO:
                        if (std::signbit(Fs1)) {
                            Rd = 1 << 3;
                        } else {
                            Rd = 1 << 4;
                        }
                        break;
                    case FP_SUBNORMAL:
                        if (std::signbit(Fs1)) {
                            Rd = 1 << 2;
                        } else {
                            Rd = 1 << 5;
                        }
                        break;
                    case FP_NORMAL:
                        if (std::signbit(Fs1)) {
                            Rd = 1 << 1;
                        } else {
                            Rd = 1 << 6;
                        }
                        break;
                    default:
                        panic("Unknown classification for operand.");
                        break;
                    }
                }});
            }
            0x78: fmv_s_x({{
                Fd_bits = (uint64_t)Rs1_uw;
            }}, FloatCvtOp);
            0x79: fmv_d_x({{
                Fd_bits = Rs1;
            }}, FloatCvtOp);
        }
    }
    0x63: decode FUNCT3 {
        format SBOp {
            0x0: beq({{
                if (Rs1 == Rs2) {
                    NPC = PC + imm;
                } else {
                    NPC = NPC;
                }
            }}, IsDirectControl, IsCondControl);
            0x1: bne({{
                if (Rs1 != Rs2) {
                    NPC = PC + imm;
                } else {
                    NPC = NPC;
                }
            }}, IsDirectControl, IsCondControl);
            0x4: blt({{
                if (Rs1_sd < Rs2_sd) {
                    NPC = PC + imm;
                } else {
                    NPC = NPC;
                }
            }}, IsDirectControl, IsCondControl);
            0x5: bge({{
                if (Rs1_sd >= Rs2_sd) {
                    NPC = PC + imm;
                } else {
                    NPC = NPC;
                }
            }}, IsDirectControl, IsCondControl);
            0x6: bltu({{
                if (Rs1 < Rs2) {
                    NPC = PC + imm;
                } else {
                    NPC = NPC;
                }
            }}, IsDirectControl, IsCondControl);
            0x7: bgeu({{
                if (Rs1 >= Rs2) {
                    NPC = PC + imm;
                } else {
                    NPC = NPC;
                }
            }}, IsDirectControl, IsCondControl);
        }
    }

    0x67: decode FUNCT3 {
        0x0: Jump::jalr({{
            Rd = NPC;
            NPC = (imm + Rs1) & (~0x1);
        }}, IsIndirectControl, IsUncondControl, IsCall);
    }

    0x6f: UJOp::jal({{
        Rd = NPC;
        NPC = PC + imm;
    }}, IsDirectControl, IsUncondControl, IsCall);

    0x73: decode FUNCT3 {
        format IOp {
            0x0: decode FUNCT12 {
                0x0: ecall({{
                    fault = std::make_shared<SyscallFault>();
                }}, IsSerializeAfter, IsNonSpeculative, IsSyscall, No_OpClass);
                0x1: ebreak({{
                    fault = std::make_shared<BreakpointFault>();
                }}, IsSerializeAfter, IsNonSpeculative, No_OpClass);
                0x100: eret({{
                    fault = std::make_shared<UnimplementedFault>("eret");
                }}, No_OpClass);
            }
            0x1: csrrw({{
                Rd = xc->readMiscReg(FUNCT12);
                xc->setMiscReg(FUNCT12, Rs1);
            }}, IsNonSpeculative, No_OpClass);
            0x2: csrrs({{
                Rd = xc->readMiscReg(FUNCT12);
                if (Rs1 != 0) {
                    xc->setMiscReg(FUNCT12, Rd | Rs1);
                }
            }}, IsNonSpeculative, No_OpClass);
            0x3: csrrc({{
                Rd = xc->readMiscReg(FUNCT12);
                if (Rs1 != 0) {
                    xc->setMiscReg(FUNCT12, Rd & ~Rs1);
                }
            }}, IsNonSpeculative, No_OpClass);
            0x5: csrrwi({{
                Rd = xc->readMiscReg(FUNCT12);
                xc->setMiscReg(FUNCT12, ZIMM);
            }}, IsNonSpeculative, No_OpClass);
            0x6: csrrsi({{
                Rd = xc->readMiscReg(FUNCT12);
                if (ZIMM != 0) {
                    xc->setMiscReg(FUNCT12, Rd | ZIMM);
                }
            }}, IsNonSpeculative, No_OpClass);
            0x7: csrrci({{
                Rd = xc->readMiscReg(FUNCT12);
                if (ZIMM != 0) {
                    xc->setMiscReg(FUNCT12, Rd & ~ZIMM);
                }
            }}, IsNonSpeculative, No_OpClass);
        }
    }
}
